Графика и разработка игр на Python
Tkinter и GUI
GUI в Python
Если задача требует создания полноценного приложения с кнопками, полями ввода, меню и другими элементами управления, необходимо использовать специализированные библиотеки для построения GUI.
Графический пользовательский интерфейс (Graphical User Interface) — это система, позволяющая пользователю взаимодействовать с программой через визуальные компоненты: окна, кнопки, списки, диаграммы и т.д. Реализация GUI в Python возможна благодаря нескольким основным библиотекам:
- Tkinter — стандартная библиотека, интегрированная в Python, построенная на Tcl/Tk.
- PyQt / PySide — привязки к фреймворку Qt, предоставляющие богатый набор виджетов.
- Kivy — кроссплатформенная библиотека для мультитач-приложений и мобильных интерфейсов.
- Dear PyGui, Flet, Eel — современные альтернативы с акцентом на производительность или web-подход.
Любое GUI-приложение следует событийно-ориентированной архитектуре:
- Создаётся главное окно.
- Добавляются виджеты (элементы управления).
- Регистрируются обработчики событий (нажатие кнопки, ввод текста).
- Запускается главный цикл (mainloop), который ожидает и распределяет события.
Пример на Tkinter
import tkinter as tk
from tkinter import messagebox
root = tk.Tk()
root.title("Пример GUI")
root.geometry("300x200")
label = tk.Label(root, text="Введите имя:")
label.pack(pady=10)
entry = tk.Entry(root)
entry.pack(pady=5)
def greet():
name = entry.get()
messagebox.showinfo("Приветствие", f"Привет, {name}!")
button = tk.Button(root, text="Приветствовать", command=greet)
button.pack(pady=10)
root.mainloop()
С развитием веб-технологий наблюдается рост популярности гибридных решений: например, использование Flask/FastAPI в качестве бэкенда и Electron-like обёрток (например, pywebview) для отображения интерфейса в браузере. Это позволяет применять современные фронтенд-фреймворки (React, Vue) вместе с Python.
Tkinter — это стандартный интерфейс языка Python к графической библиотеке Tk, построенной на основе Tcl (Tool Command Language). Он входит в состав стандартной библиотеки CPython и предоставляет доступ к кроссплатформенному набору средств для создания графических пользовательских интерфейсов (GUI). Tkinter представляет собой обёртку (binding) вокруг Tcl/Tk, что определяет как его особенности, так и ограничения.
В Python 3 Tkinter переименован в tkinter (с маленькой буквы), однако термин "Tkinter" закрепился как общее обозначение этой системы.
Архитектура Tkinter следует двухуровневой модели:
- Python-уровень: код на Python, использующий классы и методы из модуля tkinter.
- Tcl/Tk-уровень: низкоуровневый интерпретатор Tcl, в котором выполняются команды, генерируемые через Python API, и который отвечает за рендеринг виджетов и обработку событий.
Между этими уровнями существует мост (bridge), реализованный на уровне C в составе интерпретатора CPython. Этот мост обеспечивает:
- Передачу вызовов от Python к Tcl.
- Сериализацию аргументов в строковые команды Tcl.
- Получение результатов выполнения и их преобразование обратно в Python-объекты.
Например, вызов:
button = tk.Button(root, text="OK")
…внутри преобразуется в TCL-команду вроде:
button .button1 -text "OK"
… которая выполняется в рамках внутреннего интерпретатора Tcl.
«Внутренний интерпретатор»? Из каких же тогда компонентов состоит Tkinter?
Приложение Tkinter состоит из интерпретатора Tcl, главного окна и виджетов. Python-код генерирует команды, которые передаются в Tcl-интерпретатор через мост на языке C. Интерпретатор управляет отрисовкой и обработкой событий операционной системы.
| Компонент | Назначение |
|---|---|
| Интерпретатор Tcl | Выполняет команды рисования и хранит состояние виджетов |
| Главное окно (Tk) | Корневой контейнер приложения, инициализирует среду |
| Виджеты (Widgets) | Графические элементы управления (кнопки, метки, поля) |
| Геометрические менеджеры | Системы размещения виджетов (pack, grid, place) |
| Событийный цикл (mainloop) | Обрабатывает ввод пользователя и системные сообщения |
Интерпретатор Tcl
Каждый экземпляр приложения Tkinter автоматически инициализирует встроенный интерпретатор Tcl. Этот интерпретатор управляет жизненным циклом GUI, хранит состояние всех виджетов, выполняет команды рисования, обрабатывает события операционной системы (через Tk).
Важно понимать, что все виджеты существуют в пространстве имён Tcl, а не в памяти Python. Python-объекты (например, tk.Button) являются лишь обёртками, ссылающимися на соответствующие Tcl-идентификаторы.
Инициализация приложения
Запуск программы требует создания экземпляра главного окна и запуска цикла событий.
import tkinter as tk
root = tk.Tk()
root.title("Приложение")
root.geometry("400x300")
root.mainloop()
Метод mainloop() запускает бесконечный цикл ожидания событий. Выполнение кода после этого вызова происходит только после закрытия окна.
Главное окно (Tk)
Класс tk.Tk() создаёт главное окно приложения и инициирует запуск Tcl-интерпретатора. Это первый и обязательный шаг в любом Tkinter-приложении:
root = tk.Tk()
При создании экземпляра Tk происходит запуск внутреннего интерпретатора Tcl, создание корневого окна в графической подсистеме (X11, Windows GDI, macOS Aqua) и инициализация event loop.
Управление может быть передано только одному экземпляру Tk. Попытка создать второй приведёт к ошибке, если первый не был уничтожен.
Виджеты (Widgets)
Виджет — это графический элемент интерфейса: кнопка, метка, поле ввода и т.д. Каждый виджет в Tkinter является подклассом базового класса Widget. Все виджеты наследуются от BaseWidget. То есть, цепочка такова:
BaseWidget → Widget → Mixins → конкретный тип (например, Button, Label).
Виджеты принадлежат иерархии, где каждый виджет имеет родителя (обычно окно или контейнер). Идентифицируются уникальным строковым именем (например, .frame1.button2), которое используется в Tcl.
Пример создания виджета:
label = tk.Label(parent, text="Текст")
Здесь parent — другой виджет (например, Frame или Tk). При создании виджет регистрируется в Tcl-интерпретаторе под уникальным именем.
Виджет Label служит для отображения текста или изображений в окне приложения. Элемент относится к статическим компонентам интерфейса и не предполагает прямого взаимодействия пользователя через клик или ввод текста. Основная задача заключается в информировании пользователя, заголовках разделов или выводе результатов вычислений.
Объект создаётся через класс tk.Label. Первый аргумент указывает родительский контейнер.
label = tk.Label(root, text="Приветствие", fg="blue", bg="white")
label.pack()
Таблица параметров Label:
| Параметр | Описание | Пример значения |
|---|---|---|
text | Текст для отображения | "Информация" |
textvariable | Связь с объектом переменной | StringVar() |
font | Шрифт текста | ("Arial", 14, "bold") |
fg | Цвет текста (foreground) | "red", "#FF0000" |
bg | Цвет фона (background) | "yellow", "#FFFF00" |
padx | Отступ по горизонтали внутри виджета | 10 |
pady | Отступ по вертикали внутри виджета | 5 |
width | Ширина виджета в символах | 20 |
height | Высота виджета в строках | 2 |
wraplength | Длина строки для переноса текста (пиксели) | 100 |
justify | Выравнивание многострочного текста | LEFT, CENTER, RIGHT |
anchor | Позиционирование текста внутри виджета | N, S, E, W, CENTER |
image | Объект изображения для отображения | tk.PhotoImage |
compound | Расположение текста относительно изображения | TOP, BOTTOM, LEFT, RIGHT |
borderwidth | Ширина рамки вокруг виджета | 2 |
relief | Стиль рамки | FLAT, RAISED, SUNKEN, GROOVE |
cursor | Форма курсора при наведении | "hand2", "dot" |
Методы Label:
| Метод | Описание |
|---|---|
config(**options) | Изменяет параметры виджета динамически |
configure(**options) | Алиас для config |
cget(option) | Возвращает текущее значение параметра |
destroy() | Удаляет виджет из интерфейса |
place(**options) | Размещает виджет через менеджер place |
pack(**options) | Размещает виджет через менеджер pack |
grid(**options) | Размещает виджет через менеджер grid |
bind(sequence, func) | Привязывает функцию к событию |
unbind(sequence) | Удаляет привязку события |
focus_set() | Устанавливает фокус ввода на виджет |
Пример использования Label:
import tkinter as tk
root = tk.Tk()
label_title = tk.Label(
root,
text="Заголовок окна",
font=("Arial", 16, "bold"),
fg="white",
bg="black",
padx=20,
pady=10
)
label_title.pack(pady=20)
label_info = tk.Label(
root,
text="Это информационная метка.\nТекст может быть многострочным.",
justify="left",
wraplength=200
)
label_info.pack()
root.mainloop()
Динамическое обновление текста
Изменение текста метки во время работы программы требует использования объекта StringVar. Прямое изменение параметра text через config также возможно, но переменные обеспечивают автоматическую синхронизацию.
import tkinter as tk
root = tk.Tk()
text_var = tk.StringVar()
text_var.set("Начальное значение")
label = tk.Label(root, textvariable=text_var)
label.pack()
def update_text():
text_var.set("Обновлённый текст")
button = tk.Button(root, text="Изменить", command=update_text)
button.pack()
root.mainloop()
Геометрические менеджеры
Размещение виджетов на экране осуществляется с помощью геометрических менеджеров — специальных систем управления компоновкой:
a) pack(). Располагает виджеты в рамках родительского контейнера в виде потока (сверху, снизу, слева, справа). Подходит для простых линейных интерфейсов.
widget.pack(side="top", fill="x", expand=True)
b) grid(). Организует виджеты в таблицу (строки и столбцы). Наиболее гибкий и часто используемый менеджер для сложных форм.
widget.grid(row=0, column=1, padx=5, pady=5)
c) place(). Позволяет задавать абсолютные или относительные координаты. Редко используется, так как нарушает адаптивность интерфейса.
widget.place(x=100, y=50)
Важно: внутри одного контейнера нельзя смешивать разные менеджеры.
Система размещает виджеты внутри контейнера с помощью менеджеров компоновки. Каждый контейнер использует один тип менеджера для своих дочерних элементов.
Менеджер pack размещает виджеты блоками относительно сторон контейнера. Подходит для простых линейных интерфейсов.
| Параметр | Описание |
|---|---|
side | Сторона прикрепления (TOP, BOTTOM, LEFT, RIGHT) |
fill | Растягивание (X, Y, BOTH, NONE) |
expand | Занятие свободного пространства (True/False) |
padx, pady | Внешние отступы от других виджетов |
ipadx, ipady | Внутренние отступы внутри виджета |
anchor | Позиция виджета в свободном пространстве |
label.pack(side="top", fill="x", expand=True)
Менеджер grid организует виджеты в таблицу из строк и столбцов. Позволяет создавать сложные формы.
| Параметр | Описание |
|---|---|
row | Номер строки (начинается с 0) |
column | Номер столбца (начинается с 0) |
rowspan | Количество занимаемых строк |
columnspan | Количество занимаемых столбцов |
sticky | Прилипание к сторонам (N, S, E, W, NS, EW) |
padx, pady | Отступы между ячейками |
label.grid(row=0, column=0, sticky="w", padx=10, pady=5)
Менеджер place задаёт абсолютные или относительные координаты. Требует ручного расчёта позиций.
| Параметр | Описание |
|---|---|
x, y | Абсолютные координаты в пикселях |
relx, rely | Относительные координаты (0.0 - 1.0) |
width, height | Absolute размеры |
relwidth, relheight | Относительные размеры |
anchor | Точка привязки виджета к координатам |
label.place(x=50, y=100, width=200, height=50)
Обработка событий
Tkinter следует парадигме событийно-ориентированного программирования (event-driven programming). Система работает в цикле, ожидая внешних воздействий (движение мыши, нажатие клавиш, изменения размера окна и т.д.).
И основной цикл называется mainloop.
Вызов:
root.mainloop()
Он запускает бесконечный цикл, в котором:
- ОС отправляет сообщения (события) в Tk.
- Tk преобразует их в Tcl-события.
- Tcl вызывает зарегистрированные обработчики.
- Управление возвращается в Python, если обработчик задан как Python-функция.
Цикл блокирует выполнение последующего кода до закрытия окна.
События связываются с виджетами через метод bind():
widget.bind("<Button-1>", handler)
События обозначаются в угловых скобках: <KeyPress>, <Motion>, <Configure> и т.д. Обработчик получает объект события (Event), содержащий детали: координаты, клавишу, время и др.
Для кнопок и других элементов управления также используется параметр command:
button = tk.Button(root, text="Click", command=handler)
Этот способ проще, но поддерживается только для виджетов, генерирующих предопределённые действия (например, клик по кнопке).
Библиотека tkinter.ttk (Themed Tk) предоставляет доступ к современным, стилизованным виджетам, использующим нативную тему операционной системы. Виджеты из ttk выглядят естественнее и интегрированнее:
from tkinter import ttk
button = ttk.Button(root, text="Современная кнопка")
ttk использует ту же архитектуру, но рендерит виджеты с использованием системных стилей (через engine themes). Однако не все виджеты имеют ttk-аналоги, и поведение может отличаться.
Tkinter не является потокобезопасным. Все вызовы, изменяющие GUI, должны выполняться из основного потока, где работает mainloop. Если фоновый поток должен обновить интерфейс, необходимо использовать:
- Метод after(ms, func) — отложенное выполнение функции в основном потоке.
- Очереди (queue.Queue) для передачи данных между потоками.
Пример безопасного обновления:
def update_label():
if not q.empty():
value = q.get()
label.config(text=value)
root.after(100, update_label) # Повтор через 100 мс
Альтернативно — использование asyncio с интеграцией через async-tkinter-loop, но это требует сторонних библиотек.
Tkinter — это тонкая прослойка между Python и Tcl/Tk. Его архитектура определяется историческими причинами: Tcl/Tk была одной из первых кроссплатформенных GUI-библиотек, и её интеграция в Python обеспечила быстрый доступ к графическим возможностям без необходимости переписывать всё на C.
Для сложных проектов рекомендуется рассмотреть PyQt/PySide или Kivy, но понимание устройства Tkinter помогает осознать фундаментальные принципы построения GUI-приложений в Python.
Виджет Label поддерживает привязку событий через метод bind. Это позволяет реагировать на наведение мыши, клики или клавиши.
Строки событий:
| Событие | Описание |
|---|---|
<Button-1> | Нажатие левой кнопки мыши |
<Button-2> | Нажатие средней кнопки мыши |
<Button-3> | Нажатие правой кнопки мыши |
<Motion> | Движение мыши внутри виджета |
<Enter> | Курсор мыши вошёл в область виджета |
<Leave> | Курсор мыши покинул область виджета |
<Key> | Нажатие любой клавиши (при фокусе) |
<Configure> | Изменение размера виджета |
import tkinter as tk
root = tk.Tk()
label = tk.Label(root, text="Наведи на меня", bg="lightgray", width=20, height=5)
label.pack(pady=50)
def on_enter(event):
label.config(bg="lightblue", text="Я здесь")
def on_leave(event):
label.config(bg="lightgray", text="Наведи на меня")
def on_click(event):
label.config(text="Клик зарегистрирован", bg="lightgreen")
label.bind("<Enter>", on_enter)
label.bind("<Leave>", on_leave)
label.bind("<Button-1>", on_click)
root.mainloop()
Работа с изображениями
Виджет Label отображает графические файлы через объект PhotoImage. Поддерживаются форматы GIF, PGM, PPM. Для других форматов (PNG, JPG) требуется предварительная конвертация или использование библиотеки Pillow.
import tkinter as tk
root = tk.Tk()
# Загрузка изображения
photo = tk.PhotoImage(file="image.gif")
label = tk.Label(root, image=photo)
label.image = photo # Сохранение ссылки для сборщика мусора
label.pack()
root.mainloop()
Сохранение ссылки label.image = photo предотвращает удаление объекта сборщиком мусора Python. Отсутствие ссылки приводит к исчезновению изображения.
Шрифты
Модуль tkinter.font предоставляет средства управления шрифтами. Позволяет получать метрики и создавать конфигурации.
import tkinter as tk
import tkinter.font as tkFont
root = tk.Tk()
custom_font = tkFont.Font(family="Helvetica", size=12, weight="bold")
label = tk.Label(root, text="Стильный текст", font=custom_font)
label.pack()
# Получение метрик
metrics = custom_font.metrics()
print(metrics)
root.mainloop()
Параметры шрифта:
| Параметр | Описание |
|---|---|
family | Название шрифта (Arial, Times, Courier) |
size | Размер в пунктах (положительное) или пикселях (отрицательное) |
weight | Насыщенность (normal, bold) |
slant | Наклон (roman, italic) |
underline | Подчёркивание (True/False) |
overstrike | Зачёркивание (True/False) |
Классы переменных Tkinter связывают значения Python с виджетами. Изменение значения переменной автоматически обновляет интерфейс.
| Класс | Тип данных | Использование |
|---|---|---|
StringVar | Строка | Текст в Label, Entry |
IntVar | Целое число | Числовые значения, флажки |
DoubleVar | Число с плавающей точкой | Точные числа |
BooleanVar | Булево значение | Чекбоксы, переключатели |
import tkinter as tk
root = tk.Tk()
var = tk.IntVar()
var.set(10)
label = tk.Label(root, textvariable=var)
label.pack()
def increment():
current = var.get()
var.set(current + 1)
button = tk.Button(root, text="+", command=increment)
button.pack()
root.mainloop()
Примеры реализации
Таймер обратного отсчёта
Использование метода after для планирования задач.
import tkinter as tk
def countdown(count):
label_text.set(f"Осталось: {count}")
if count > 0:
root.after(1000, countdown, count - 1)
else:
label_text.set("Время вышло")
root = tk.Tk()
root.geometry("300x200")
label_text = tk.StringVar()
label = tk.Label(root, textvariable=label_text, font=("Arial", 24))
label.pack(pady=50)
countdown(10)
root.mainloop()
Метка с рамкой и тенью
Комбинирование параметров для визуального оформления.
import tkinter as tk
root = tk.Tk()
root.config(bg="gray")
label = tk.Label(
root,
text="Эффектная метка",
font=("Arial", 16),
bg="white",
fg="black",
padx=20,
pady=10,
relief="raised",
borderwidth=4
)
label.pack(pady=50)
root.mainloop()
Выравнивание текста
Демонстрация параметров anchor и justify.
import tkinter as tk
root = tk.Tk()
text_long = "Это длинный текст для демонстрации выравнивания.\nОн занимает несколько строк."
# Выравнивание содержимого внутри метки
label1 = tk.Label(root, text=text_long, justify="left", anchor="w", width=40, bg="lightblue")
label1.pack(fill="x", padx=10, pady=5)
# Выравнивание самой метки в контейнере
label2 = tk.Label(root, text="Центр", width=10, bg="lightgreen")
label2.pack(anchor="center", pady=5)
label3 = tk.Label(root, text="Право", width=10, bg="lightyellow")
label3.pack(anchor="e", pady=5)
root.mainloop()
Смена изображений по клику
import tkinter as tk
def switch_image():
global current_img
if current_img == photo1:
label.config(image=photo2)
current_img = photo2
else:
label.config(image=photo1)
current_img = photo1
root = tk.Tk()
photo1 = tk.PhotoImage(file="img1.gif")
photo2 = tk.PhotoImage(file="img2.gif")
current_img = photo1
label = tk.Label(root, image=photo1)
label.pack(pady=20)
label.bind("<Button-1>", lambda e: switch_image())
root.mainloop()
Turtle
Черепашья графика
Графический интерфейс в языке программирования Python может быть реализован различными способами — от простой черепашьей графики до полноценных 2D-приложений и сложных GUI-систем. Сначала поговорим о простой графике.
В жизни каждого специалиста возникают ситуации-конфузы. Так вот, после многих лет разработки на языках C#, Java, JavaScript, Python я встречаю некую «черепашью графику» в Python, причём в курсе программирования для детей. У меня конфуз - что за черепашья графика? Почему графика? Что имеется в виду? GUI? 2D/3D? Вектор/растр?
Потом ещё веселее - объекты в «черепашьей графике». Объектно-ориентированное программирование? Да? Или нет?
Оказалось, что есть такое необычное изобретение в истории обучения программированию. Профессионалы учатся немного иначе, тогда как новичков и детей учат совсем простым вещам, о которых мы даже не задумываемся.
Так вот, черепашья графика (Turtle Graphics) - это визуальный способ программирования, при котором мы управляем виртуальной черепашкой, которая ползёт по экрану и рисует линии. Она как художник с карандашом: куда она ползёт — там остаётся след. Это простая 2D-графика, где учатся программировать через наглядные действия вроде «иди вперёд», «поверни налево», «подними карандаш» и т.д.
Название происходит от языка Logo, который был разработан в 1960-х годах для обучения детей программированию. В нём была физическая игрушечная черепаха на полу, которая двигалась по командам и рисовала мелом. Потом эта идея перешла в виртуальную черепашку на экране.
В Python модуль turtle — это современная реализация этой идеи. Он встроен в стандартную библиотеку, то есть не нужно ничего устанавливать — просто пишем:
import turtle
И начинаем рисовать.
Базовая структура скрипта включает импорт, создание объектов, выполнение команд и завершение работы.
import turtle
# Создание экрана
screen = turtle.Screen()
# Создание черепахи
pen = turtle.Turtle()
# Команды рисования
pen.forward(100)
# Завершение работы
turtle.done()
Модуль turtle входит в стандартную библиотеку Python и предоставляет простую, но мощную систему для работы с двумерной графикой. Основная идея заключается в концепции «черепахи» — абстрактного исполнителя, перемещающегося по координатной плоскости и оставляющего за собой след. Через последовательность команд можно управлять направлением движения, положением, цветом линии и другими параметрами.
Задумка следующая - на экране появляется маленькая стрелочка (или черепашка, если включить соответствующую форму). У неё есть:
- Позиция (x, y) — где она находится.
- Направление — куда смотрит (например, вверх, вправо).
- Ручка (pen) — опущена (рисует) или поднята (не рисует).
- Цвет линии, цвет заливки, толщина линии, скорость движения.
Задача как раз в управлении «черепашкой» при помощи команд.
Чтобы окно не закрывалось сразу, нужно добавлять в конец кода:
turtle.done()
Функция turtle.done() запускает главный цикл обработки событий. Окно остаётся открытым до момента закрытия пользователем. Отсутствие этого вызова приводит к мгновенному закрытию окна после выполнения скрипта.
Команды там следующие:
- forward(100) - Черепашка идёт вперёд на 100 пикселей;
- backward(50) - Назад на 50 пикселей;
- left(90) - Поворачивает налево на 90 градусов;
- right(45) - Направо на 45°;
- penup() - Поднимает ручку — больше не рисует;
- pendown() - Опускает ручку — снова рисует;
- goto(x, y) - Мгновенно перемещается в точку (x, y);
- color("red") - Устанавливает цвет ручки и заливки;
- pencolor("blue") - Только цвет линии;
- fillcolor("yellow") - Цвет заливки фигур;
- begin_fill() / end_fill() - Начинает и заканчивает заливку области;
- speed(5) - Скорость от 1 (медленно) до 10 (быстро), 0 — мгновенно;
- shape("turtle") - Меняет внешний вид: "arrow", "circle", "turtle" и др.
Пример: рисование квадрата.

Это как цикл for в C#, только вместо изменения переменной мы меняем состояние объекта.
Хотя это не GUI с кнопками, это графика в математическом смысле: работа с координатной плоскостью, углами, фигурами, цветами, траекториями. Экран является координатной плоскостью, где центр (0,0) - середина экрана, X вправо, Y вверх, как в базовой математике.
Так рисуют геометрические фигуры, используя циклы и функции, можно даже фракталы, узоры, анимации.
Здесь каждая черепашка — объект класса Turtle.
t1 = turtle.Turtle()
t2 = turtle.Turtle()
t1.color("red")
t2.color("blue")
t1.goto(-100, 0)
t2.goto(100, 0)
Технически, модуль turtle содержит в себе класс Turtle и при помощи конструктора Turtle() создаётся черепашка как объект, имеющий свойства и поведение. Концепция как раз в том, чтобы использовать методы черепашки для управления и рисования фигур.
t1 и t2 образуют две независимые «кисти», которые могут двигаться параллельно, рисовать разными цветами, иметь разную скорость и выполнять разные программы. Это отличный способ показать детям (и напомнить взрослым), что объекты — это экземпляры класса с собственным состоянием и поведением.
Чтобы не писать 360 строк для круга, можно использовать цикл, и получить почти круг (технически это 360-угольник). Можно делать розетки, спирали, снежинки Коха — всё через повторение простых действий.
for _ in range(360):
t.forward(1)
t.left(1)
Пример - две черепахи навстречу.
t1 = turtle.Turtle()
t2 = turtle.Turtle()
t1.penup()
t1.goto(-200, 0)
t1.pendown()
t1.color("red")
t2.penup()
t2.goto(200, 0)
t2.pendown()
t2.color("blue")
t2.setheading(180) # смотрит влево
# Ползут навстречу
for _ in range(100):
t1.forward(2)
t2.forward(2)
Так можно наглядно увидеть, что каждый объект живёт своей жизнью.
В объектно-ориентированной парадигме turtle представляет собой класс Turtle, экземпляры которого обладают состоянием (позиция, угол поворота, цвет пера, видимость) и поведением (методы перемещения, рисования, изменения стиля). Каждая черепаха работает в рамках общего холста — экземпляра класса Screen, который управляет окном вывода, фоном и событиями.
Черепаха может быть склонирована, переопределена или инкапсулирована в пользовательские классы, что позволяет строить более сложные графические системы.
Система координат в turtle — прямоугольная, с началом в центре окна. Положительное направление оси X — вправо, оси Y — вверх. Размеры окна задаются явно через метод setup(width, height) класса Screen. Координаты могут быть абсолютными (например, goto(x, y)) или относительными (например, forward(distance)).
Ориентация угла измеряется в градусах против часовой стрелки от положительного направления оси X. Поворот осуществляется командами left(angle) и right(angle).
Модуль поддерживает широкий спектр способов задания цветов:
- Строковые имена: "red", "blue", "green" и др.
- RGB-значения в формате кортежа (r, g, b), где каждое значение — число от 0 до 1 (или от 0 до 255 при активации режима colormode(255)).
- HEX-коды: "#FF5733".
Для заливки фигур используется комбинация методов begin_fill() и end_fill(), которые фиксируют контур и применяют текущий цвет заполнения.
Например, можно нарисовать сердечко:
import turtle
import math
t = turtle.Turtle()
t.speed(0)
screen = turtle.Screen()
screen.bgcolor("white")
t.begin_fill()
t.fillcolor("red")
t.left(50)
t.forward(133)
t.circle(50, 200)
t.right(140)
t.circle(50, 200)
t.forward(133)
t.end_fill()
t.penup()
t.hideturtle()
turtle.done()
Анимация в turtle реализуется через последовательное изменение состояния черепахи с контролем скорости выполнения. Метод speed(n) позволяет установить скорость от 0 (мгновенно) до 10 (медленно). Для плавной анимации используются циклы с задержками, хотя сам модуль не является реалтайм-движком.
Обработка событий (например, нажатия клавиш, кликов мыши) осуществляется через регистрацию обработчиков:
def move_forward():
t.forward(10)
screen.onkey(move_forward, "Up")
screen.listen()
Здесь важно понимать, что turtle использует внутренний event loop, запускаемый вызовом screen.mainloop() — без него события не будут обрабатываться.
Поэтому всегда нужно добавлять в код следующее:
screen.listen() # ← ВАЖНО! Без этого не будет работать
К примеру, так можно реализовать управление:
import turtle
screen = turtle.Screen()
screen.listen()
t = turtle.Turtle()
t.shape("turtle")
t.color("green")
def go_up():
t.setheading(90)
t.forward(20)
def go_down():
t.setheading(270)
t.forward(20)
def go_left():
t.setheading(180)
t.forward(20)
def go_right():
t.setheading(0)
t.forward(20)
screen.onkey(go_up, "Up")
screen.onkey(go_down, "Down")
screen.onkey(go_left, "Left")
screen.onkey(go_right, "Right")
screen.onkey(screen.bye, "q")
print("Управление: стрелки — движение, Q — выход")
turtle — однопоточная система; длительные операции блокируют интерфейс, и этот модуль не предназначен для производительных графических задач. Использует Tkinter в качестве бэкенда, что делает его зависимым от наличия GUI-среды. Подходит для обучения, прототипирования и демонстрации алгоритмов, но не для промышленных приложений.
Класс Screen
Объект Screen управляет окном приложения. Через него задаются размеры, цвет фона, заголовок и обработчики событий.
| Метод | Параметры | Описание |
|---|---|---|
turtle.Screen() | — | Создаёт объект главного окна |
setup(width, height) | числа | Устанавливает размеры окна в пикселях |
title(string) | строка | Задаёт заголовок окна |
bgcolor(color) | цвет | Устанавливает цвет фона |
bgpic(picname) | путь к файлу | Устанавливает изображение фона |
mode(mode) | "standard", "logo", "parent" | Выбирает режим координат и углов |
tracer(n) | число | Управляет анимацией отрисовки (0 выключает) |
update() | — | Обновляет экран при выключенном трассировщике |
exitonclick() | — | Закрывает окно по клику |
bye() | — | Закрывает окно программно |
Экран регистрирует нажатия клавиш и клики мыши. Обработчики событий представляют собой функции, вызываемые при наступлении события.
def move():
pen.forward(50)
screen.onkey(move, "space")
screen.listen()
Вызов screen.listen() активирует прослушивание событий клавиатуры. Без этого вызова обработчики не срабатывают.
Класс Turtle
Объект Turtle представляет собой исполнителя. Каждый экземпляр класса обладает собственным состоянием: координатами, углом поворота, цветом пера, видимостью.
Движение и положение:
| Метод | Параметры | Описание |
|---|---|---|
forward(distance) | число | Движение вперёд на указанное расстояние |
backward(distance) | число | Движение назад |
right(angle) | число | Поворот направо на угол в градусах |
left(angle) | число | Поворот налево на угол в градусах |
goto(x, y) | числа | Перемещение в точку с координатами (x, y) |
setx(x) | число | Установка координаты X |
sety(y) | число | Установка координаты Y |
setheading(angle) | число | Установка абсолютного угла направления |
home() | — | Возврат в центр (0, 0) и направление вверх |
circle(radius) | число | Рисование круга заданного радиуса |
dot(size, color) | число, цвет | Рисование точки |
stamp() | — | Оставляет отпечаток черепахи на холсте |
clearstamp(id) | число | Удаляет отпечаток по идентификатору |
position() | — | Возвращает текущие координаты (x, y) |
heading() | — | Возвращает текущий угол поворота |
distance(obj) | объект | Возвращает расстояние до другого объекта |
Управление пером:
| Метод | Параметры | Описание |
|---|---|---|
pendown() | — | Опускает перо (рисует при движении) |
penup() | — | Поднимает перо (не рисует при движении) |
pensize(width) | число | Устанавливает толщину линии |
pencolor(color) | цвет | Устанавливает цвет линии |
fillcolor(color) | цвет | Устанавливает цвет заливки |
color(color) | цвет | Устанавливает цвет пера и заливки |
begin_fill() | — | Начинает запись контура для заливки |
end_fill() | — | Завершает заливку замкнутого контура |
filling() | — | Возвращает статус заливки (True/False) |
reset() | — | Сбрасывает состояние черепахи и очищает экран |
clear() | — | Очищает рисунки, сохраняет положение |
undo() | — | Отменяет последнее действие |
speed(value) | 0-10 | Устанавливает скорость (0 — мгновенно) |
hideturtle() | — | Скрывает курсор черепахи |
showturtle() | — | Показывает курсор черепахи |
isvisible() | — | Возвращает видимость курсора |
Координатная система
По умолчанию используется декартова система координат. Начало отсчёта (0, 0) находится в центре окна. Ось X направлена вправо, ось Y направлена вверх. Углы измеряются в градусах. Нулевой угол направлен вправо (восток), отсчёт идёт против часовой стрелки.
Режим standard устанавливает направление 0 градусов вправо.
Режим logo устанавливает направление 0 градусов вверх.
Координаты принимают целые и дробные значения. Метод goto() перемещает объект в указанную точку. Методы setx() и sety() меняют одну из координат.
Обработка ввода и событий
Система событий реагирует на действия пользователя. Регистрация обработчиков происходит через объект Screen.
Метод onkey(fun, key) связывает функцию с клавишей. Срабатывание происходит при отпускании клавиши. Метод onkeypress(fun, key) срабатывает при нажатии и удержании (требует вызова listen()).
Регистр символов имеет значение. Клавиша "A" и клавиша "a" представляют собой разные события. Для букв нижнего регистра используется строчное обозначение. Для специальных клавиш используются строковые константы.
| Клавиша | Обозначение в коде |
|---|---|
| Пробел | "space" |
| Вверх | "Up" |
| Вниз | "Down" |
| Влево | "Left" |
| Вправо | "Right" |
| Enter | "Return" |
| Esc | "Escape" |
| Tab | "Tab" |
| Backspace | "BackSpace" |
| Delete | "Delete" |
| Home | "Home" |
| End | "End" |
| Page Up | "Page_Up" |
| Page Down | "Page_Down" |
| F1 - F12 | "F1" ... "F12" |
| Буквы | "a" ... "z", "A" ... "Z" |
| Цифры | "0" ... "9" |
| Плюс | "plus" |
| Минус | "minus" |
| Точка | "period" |
Пример регистрации клавиш:
def move_up():
pen.setheading(90)
pen.forward(20)
screen.onkey(move_up, "Up")
screen.onkey(move_up, "w")
screen.listen()
Обработка мыши позволяет реагировать на клики в любом месте экрана или на объектах.
| Метод | Описание |
|---|---|
onclick(fun) | Вызывает функцию при клике на черепаху |
onrelease(fun) | Вызывает функцию при отпускании кнопки на черепахе |
ondrag(fun) | Вызывает функцию при перетаскивании черепахи |
onscreenclick(fun) | Вызывает функцию при клике по экрану |
Функция обработчика клика получает координаты события x и y.
def go_to(x, y):
pen.goto(x, y)
screen.onscreenclick(go_to)
Графика и внешний вид
Система поддерживает несколько форматов задания цвета.
- Строковые имена:
"red","green","blue","yellow","black","white","purple","orange"и другие стандартные названия. - RGB кортеж:
(r, g, b). Значения находятся в диапазоне от 0 до 1 по умолчанию. - RGB 255: Требуется вызов
screen.colormode(255). Значения находятся в диапазоне от 0 до 255. - HEX: Строки вида
"#FF5733".
Пример настройки цвета:
screen.colormode(255)
pen.pencolor((255, 0, 0)) # Красный
pen.fillcolor("#00FF00") # Зелёный
Курсор черепахи имеет форму. Доступны встроенные формы и пользовательские изображения.
| Форма | Обозначение |
|---|---|
| Стрелка | "arrow" |
| Черепаха | "turtle" |
| Круг | "circle" |
| Квадрат | "square" |
| Треугольник | "triangle" |
| Классическая | "classic" |
Метод shape(name) меняет внешний вид курсора.
Использование собственных изображений требует регистрации формы в объекте экрана. Файл изображения должен находиться в рабочей директории или путь должен быть указан полностью. Поддерживаются форматы GIF, PNG (зависит от версии Tkinter).
screen.register_shape("ship.gif")
pen.shape("ship.gif")
Изображение становится доступным для всех черепах после регистрации. Центр изображения совпадает с координатами черепахи.
Модуль позволяет выводить текст на холст через метод write().
| Параметр | Описание |
|---|---|
arg | Текст для вывода |
move | Boolean. Перемещает ли черепаху после вывода |
align | Выравнивание: "left", "center", "right" |
font | Кортеж (шрифт, размер, стиль) |
Пример вывода текста:
pen.write("Старт", align="center", font=("Arial", 16, "bold"))
Стили шрифта: "normal", "bold", "italic", "bold italic".
Логика и циклы
Использование циклов позволяет создавать повторяющиеся узоры и фигуры. Конструкция for i in range() задаёт количество повторений.
Сумма внешних углов любого замкнутого многоугольника равна 360 градусам. Угол поворота для правильного многоугольника вычисляется как 360, делённое на количество сторон.
sides = 6
angle = 360 / sides
for i in range(sides):
pen.forward(100)
pen.right(angle)
Изменение расстояния или угла внутри цикла создаёт спираль.
for i in range(100):
pen.forward(i * 2)
pen.right(90)
Модуль random генерирует случайные числа для вариативности рисунка.
import random
colors = ["red", "blue", "green", "yellow"]
for i in range(50):
pen.pencolor(random.choice(colors))
pen.forward(random.randint(50, 100))
pen.right(random.randint(0, 360))
Скрипт реализует движение объекта стрелками клавиатуры.
import turtle
screen = turtle.Screen()
screen.setup(500, 500)
screen.title("Управление")
player = turtle.Turtle()
player.shape("turtle")
player.penup()
player.speed(0)
def move_up():
player.setheading(90)
player.forward(20)
def move_down():
player.setheading(270)
player.forward(20)
def move_left():
player.setheading(180)
player.forward(20)
def move_right():
player.setheading(0)
player.forward(20)
screen.onkey(move_up, "Up")
screen.onkey(move_down, "Down")
screen.onkey(move_left, "Left")
screen.onkey(move_right, "Right")
screen.listen()
turtle.done()
Пример создания закрашенного круга.
import turtle
screen = turtle.Screen()
pen = turtle.Turtle()
pen.fillcolor("blue")
pen.begin_fill()
pen.circle(50)
pen.end_fill()
turtle.done()
Программа рисует линии вслед за курсором мыши при нажатой кнопке.
import turtle
screen = turtle.Screen()
pen = turtle.Turtle()
pen.speed(0)
def drag(x, y):
pen.ondrag(None) # Блокировка рекурсии
pen.goto(x, y)
pen.ondrag(drag) # Возврат обработчика
pen.ondrag(drag)
screen.listen()
turtle.done()
Метод ontimer(fun, delay) запускает функцию через указанный интервал времени в миллисекундах. Это создаёт эффект анимации без блокировки цикла.
import turtle
screen = turtle.Screen()
pen = turtle.Turtle()
angle = 0
def rotate():
global angle
pen.clear()
pen.setheading(angle)
pen.forward(50)
pen.stamp()
angle += 10
screen.ontimer(rotate, 100) # Запуск через 100 мс
pen.speed(0)
rotate()
turtle.done()
Проверка расстояния между объектами позволяет реализовать простую коллизию.
import turtle
import random
screen = turtle.Screen()
player = turtle.Turtle()
target = turtle.Turtle()
player.shape("turtle")
player.penup()
target.shape("circle")
target.color("red")
target.penup()
target.goto(random.randint(-200, 200), random.randint(-200, 200))
def move():
player.forward(20)
if player.distance(target) < 20:
target.goto(random.randint(-200, 200), random.randint(-200, 200))
screen.onkey(move, "space")
screen.listen()
turtle.done()
Отрисовка каждого кадра анимации может замедлять работу программы. Метод tracer(0) отключает автоматическое обновление экрана. Обновление происходит только при вызове screen.update().
screen.tracer(0)
for i in range(100):
pen.forward(1)
# Экран не обновляется здесь
screen.update() # Обновление один раз в конце
Этот подход устраняет мерцание и повышает производительность при большом количестве объектов.
Работа с несколькими черепахами
Создание нескольких экземпляров класса Turtle позволяет управлять независимыми объектами.
t1 = turtle.Turtle()
t2 = turtle.Turtle()
t1.color("red")
t2.color("blue")
t1.goto(-50, 0)
t2.goto(50, 0)
t1.circle(30)
t2.circle(30)
Каждая черепаха хранит своё состояние независимо. Команды, отправленные одному объекту, не влияют на другие.
Сохранение и экспорт
Модуль turtle не имеет встроенной функции сохранения холста в файл изображения напрямую через стандартный API. Для сохранения результата используется скриншот операционной системы или сторонние библиотеки захвата экрана. Состояние программы (позиции, переменные) сохраняется через стандартные средства Python (модуль pickle или запись в файл).
Ограничения среды
Библиотека зависит от Tkinter. Запуск скрипта требует наличия графической среды. На серверах без графического интерфейса (headless) выполнение кода вызовет ошибку. Производительность ограничена возможностями интерпретатора Python и скоростью отрисовки Tkinter. Обработка большого количества объектов (более 100-200 активных спрайтов) приводит к снижению частоты кадров. Для сложных игр рекомендуется использовать специализированные движки.
Разработка игр
Основы разработки игр на Python
Python не является основным языком в индустрии разработки игр. Его динамическая типизация, интерпретируемая природа и относительно низкая производительность делают его малопригодным для создания ресурсоёмких проектов — таких как AAA-игры или масштабные многопользовательские онлайн-платформы. Однако Python остаётся мощным инструментом в контексте обучения, прототипирования, скриптования игровой логики и реализации простых 2D-приложений.
Для этих целей существует ряд библиотек, позволяющих организовать графический вывод, обработку ввода, аудиосопровождение и базовую физику. Наиболее известной и долгоживущей из них является Pygame — кроссплатформенная библиотека, построенная на основе SDL (Simple DirectMedia Layer). Она предоставляет минимально необходимый набор средств для создания 2D-игр и используется преимущественно в образовательных целях, а также в инди-проектах небольшого масштаба.
Python не предназначен для создания игровых движков уровня Unreal Engine или Unity. Тем не менее, он может эффективно применяться как средство обучения основам геймдева, анализа алгоритмов поведения персонажей, реализации ИИ в играх, а также как платформа для быстрого прототипирования механик.
Что касается других технологий, то Godot поддерживает язык GDScript, синтаксически близкий к Python, но это не Python. Начиная с версии 4.0, Godot также предлагает официальную поддержку Python через модуль GDExtension (на базе Cython), однако использование Python в Godot — скорее исключение, чем правило. Основная экосистема Godot ориентирована на GDScript, C# и C++. Таким образом, утверждение о том, что Godot «использует Python» — упрощение, не соответствующее действительности в полной мере.
В рамках данной главы мы сосредоточимся на Pygame — наиболее доступной и документированной библиотеке для разработки 2D-игр на чистом Python.
Pygame — это набор модулей Python, предоставляющий доступ к низкоуровневым функциям мультимедиа через привязки к библиотеке SDL. Он позволяет работать с графикой, звуком, вводом с клавиатуры, мыши и геймпадов, а также обеспечивает базовые средства для обработки времени и столкновений.
Библиотека не является игровым движком в строгом смысле слова. У неё отсутствуют встроенные системы анимаций, физики, сцен, шейдеров или редактор ресурсов. Вместо этого Pygame предоставляет низкоуровневые примитивы, которые разработчик должен компоновать самостоятельно, реализуя игровую логику «с нуля».
Тем не менее, именно эта особенность делает Pygame ценным инструментом для обучения: он заставляет разработчика понимать внутренние механизмы работы игрового цикла, обработки событий и управления состоянием объектов.
Pygame
Pygame представляет собой набор модулей Python, предоставляющий доступ к низкоуровневым функциям мультимедиа через привязки к библиотеке SDL (Simple DirectMedia Layer). Библиотека обеспечивает работу с графикой, звуком, вводом с клавиатуры, мыши и геймпадов. Система включает средства обработки времени и обнаружения столкновений.
Библиотека строится на основе SDL, написанной на языке C. Python-код выступает обёрткой, передающей команды в низкоуровневую среду. Это определяет производительность и кроссплатформенность. Поддерживаются операционные системы Windows, macOS, Linux.
Установка выполняется через менеджер пакетов pip:
pip install pygame
Проверка установки возможна через вывод версии модуля:
import pygame
print(pygame.ver)
Любое приложение на Pygame содержит базовый набор вызовов. Отсутствие любого из них приводит к ошибке выполнения или некорректной работе графического интерфейса.
- Импорт модуля:
import pygame. - Инициализация:
pygame.init(). Запускает внутренние подсистемы (видео, аудио, ввод). - Создание окна:
pygame.display.set_mode(). Возвращает объект поверхности экрана. - Игровой цикл: Бесконечный цикл
while, обрабатывающий события и обновляющий кадр. - Завершение:
pygame.quit()и выход из интерпретатора.
Пример минимальной структуры:
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Окно игры")
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((0, 0, 0))
pygame.display.flip()
pygame.quit()
Библиотека разделена на логические модули. Каждый модуль отвечает за конкретную задачу.
| Модуль | Назначение | Ключевые объекты |
|---|---|---|
pygame.display | Управление окном и экраном | Surface, set_mode, flip, update |
pygame.draw | Рисование примитивов | circle, rect, line, polygon |
pygame.font | Работа со шрифтами и текстом | Font, render, SysFont |
pygame.event | Обработка событий ввода | get, poll, QUIT, KEYDOWN |
pygame.time | Контроль времени и кадров | Clock, tick, get_ticks |
pygame.image | Загрузка и сохранение изображений | load, save, fromstring |
pygame.mixer | Воспроизведение звука | Sound, music, play, stop |
pygame.sprite | Управление игровыми объектами | Sprite, Group, collide |
pygame.rect | Прямоугольники и коллизии | Rect, colliderect, move |
pygame.key | Состояние клавиатуры | get_pressed, name |
pygame.mouse | Состояние мыши | get_pos, get_pressed, set_visible |
Вызов pygame.init() активирует все доступные модули. Существует возможность инициализации отдельных подсистем для экономии ресурсов, например pygame.display.init() или pygame.mixer.init(). Функция pygame.get_init() возвращает статус инициализации. Функция pygame.quit() выключает все модули.
Окно и поверхности (Surfaces)
Модуль display контролирует графическое окно. Центральным объектом выступает поверхность (Surface), возвращаемая функцией set_mode().
Функция set_mode(size, flags=0, depth=0) принимает кортеж размеров (ширина, высота). Флаги определяют режим отображения.
| Флаг | Описание |
|---|---|
pygame.FULLSCREEN | Разворачивает окно на весь экран |
pygame.RESIZABLE | Позволяет пользователю менять размер окна |
pygame.NOFRAME | Убирает рамку и заголовок окна |
pygame.HIDDEN | Создаёт окно в скрытом режиме |
Центральным понятием в Pygame является поверхность (Surface) — двумерный массив пикселей, представляющий собой область рисования. Каждое изображение, текст или графический элемент в Pygame является экземпляром pygame.Surface.
# Создание новой поверхности размером 100x100 с прозрачностью
surf = pygame.Surface((100, 100), pygame.SRCALPHA)
surf.fill((255, 0, 0, 128)) # Красный цвет с полупрозрачностью
Главное окно игры создаётся с помощью pygame.display.set_mode(), которое возвращает объект Surface, связанный с экраном. Все последующие операции рисования (blitting) выполняются на этой или других поверхностях.
import pygame
screen = pygame.display.set_mode((800, 600))
Поверхности могут быть прозрачными (с альфа-каналом), иметь цветовой ключ (colorkey) для маскирования фона, а также подвергаться трансформациям: масштабированию, повороту, отражению.
Изменения на поверхности не отображаются автоматически. Требуется явный вызов функции обновления.
| Функция | Описание |
|---|---|
pygame.display.flip() | Обновляет весь экран. Подходит для двойной буферизации |
pygame.display.update(rectangle) | Обновляет указанную область экрана. Экономит ресурсы |
Заголовок и иконка тоже могут быть изменены:
pygame.display.set_caption("Название игры")
icon = pygame.image.load("icon.png")
pygame.display.set_icon(icon)
Координатная система
В Pygame начало координат (0, 0) находится в левом верхнем углу экрана. Ось X направлена вправо. Ось Y направлена вниз.
Это отличается от стандартной математической системы, где Y направлен вверх. При расчёте физики необходимо учитывать инверсию оси Y.
Размеры окна задаются в пикселях. Координаты объектов хранятся в целых числах. Дробные координаты возможны внутри логики игры, но отрисовка требует целочисленных значений.
Рисование
Модуль draw содержит функции для рисования геометрических фигур непосредственно на поверхности.
| Функция | Параметры | Описание |
|---|---|---|
rect | surface, color, rect, width=0 | Рисует прямоугольник. width=0 заполняет фигуру |
polygon | surface, color, points, width=0 | Рисует многоугольник по списку точек |
circle | surface, color, center, radius, width=0 | Рисует круг |
ellipse | surface, color, rect, width=0 | Рисует эллипс внутри прямоугольника |
arc | surface, color, rect, start_angle, stop_angle, width=1 | Рисует дугу |
line | surface, color, start_pos, end_pos, width=1 | Рисует линию между двумя точками |
lines | surface, color, closed, points, width=1 | Рисует набор соединённых линий |
aaline | surface, color, start_pos, end_pos, blend=1 | Рисует сглаженную линию |
aalines | surface, color, closed, points, blend=1 | Рисует набор сглаженных линий |
Пример рисования фигуры:
pygame.draw.rect(screen, (0, 255, 0), (50, 50, 100, 100))
pygame.draw.circle(screen, (0, 0, 255), (200, 200), 50)
Класс pygame.Rect хранит координаты и размеры прямоугольной области. Используется для позиционирования и проверки столкновений.
# Из координат и размеров
rect = pygame.Rect(x, y, width, height)
# Из поверхности
rect = surface.get_rect()
# Из кортежа
rect = pygame.Rect((10, 10, 50, 50))
Объект Rect обладает набором атрибутов для доступа к координатам сторон и углов.
| Атрибут | Описание |
|---|---|
x, y | Координаты левого верхнего угла |
width, height | Размеры прямоугольника |
top, left, bottom, right | Координаты сторон |
center, centerx, centery | Координаты центра |
topleft, topright, bottomleft, bottomright | Координаты углов |
midtop, midleft, midbottom, midright | Координаты середин сторон |
size | Кортеж (width, height) |
w, h | Короткие aliases для width и height |
Методы Rect:
| Метод | Описание |
|---|---|
colliderect(other_rect) | Возвращает True при пересечении с другим Rect |
collidepoint(x, y) | Возвращает True, если точка внутри Rect |
collidelist(list) | Проверяет пересечение со списком Rect |
move(x, y) | Возвращает новый Rect, смещённый на x, y |
move_ip(x, y) | Смещает текущий Rect на x, y (in place) |
clamp(other_rect) | Перемещает Rect внутрь другого Rect |
clip(other_rect) | Возвращает пересечение двух Rect |
union(other_rect) | Возвращает объединение двух Rect |
unionall(list) | Возвращает объединение со списком Rect |
fit(other_rect) | Масштабирует и перемещает Rect внутрь другого |
normalize() | Исправляет отрицательную ширину или высоту |
Текст и шрифты (font)
Модуль font позволяет рендерить текст на поверхностях. Текст в Pygame является изображением.
# Системный шрифт
font = pygame.font.SysFont("Arial", 24)
# Загрузка из файла
font = pygame.font.Font("file.ttf", 24)
# Шрифт по умолчанию
font = pygame.font.Font(None, 24)
Метод render(text, antialias, color, background=None) создаёт поверхность с текстом.
| Параметр | Описание |
|---|---|
text | Строка для отображения |
antialias | Boolean. Сглаживание границ букв |
color | Цвет текста (RGB или RGBA) |
background | Цвет фона текста (опционально) |
Пример вывода текста:
text_surface = font.render("Счёт: 10", True, (255, 255, 255))
screen.blit(text_surface, (10, 10))
Доступные свойства Font:
| Свойство | Описание |
|---|---|
get_height() | Высота символов в пикселях |
get_linesize() | Высота строки |
get_ascent() | Подъём шрифта над базовой линией |
get_descent() | Спуск шрифта под базовую линию |
size(text) | Возвращает кортеж (width, height) текста |
metrics(text) | Возвращает метрики для каждого символа |
set_bold(bool) | Устанавливает жирное начертание |
set_italic(bool) | Устанавливает курсив |
set_underline(bool) | Устанавливает подчёркивание |
get_bold(), get_italic(), get_underline() | Возвращают статус стилей |
События и ввод (event)
Система событий обрабатывает действия пользователя и системы. События помещаются в очередь.
for event in pygame.event.get():
# Обработка
Функция pygame.event.get() возвращает список событий за последний кадр. Функция pygame.event.poll() возвращает одно событие или pygame.NOEVENT.
Типы событий:
| Тип | Описание |
|---|---|
pygame.QUIT | Пользователь нажал кнопку закрытия окна |
pygame.KEYDOWN | Клавиша нажата |
pygame.KEYUP | Клавиша отпущена |
pygame.MOUSEMOTION | Движение мыши |
pygame.MOUSEBUTTONDOWN | Нажатие кнопки мыши |
pygame.MOUSEBUTTONUP | Отпускание кнопки мыши |
pygame.JOYAXISMOTION | Движение оси геймпада |
pygame.JOYBUTTONDOWN | Нажатие кнопки геймпада |
Событие KEYDOWN содержит атрибут key (код клавиши) и mod (модификаторы Shift, Ctrl).
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
jump()
if event.key == pygame.K_ESCAPE:
running = False
Постоянное состояние клавиш проверяется через pygame.key.get_pressed(). Возвращает кортеж булевых значений для всех клавиш.
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
player.x -= speed
Событие MOUSEBUTTONDOWN содержит атрибут button (1 - левая, 3 - правая, 2 - колесо). Атрибут pos содержит координаты (x, y).
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
shoot(event.pos)
Текущая позиция мыши: pygame.mouse.get_pos().
Состояние кнопок: pygame.mouse.get_pressed().
Для геймпадов же требуется инициализация модуля pygame.joystick.
pygame.joystick.init()
joystick_count = pygame.joystick.get_count()
if joystick_count > 0:
joystick = pygame.joystick.Joystick(0)
joystick.init()
В цикле событий обрабатываются JOYAXISMOTION и JOYBUTTONDOWN. Оси возвращают значения от -1.0 до 1.0.
Время и частота кадров (time)
Модуль time контролирует скорость игры.
Объект pygame.time.Clock() регулирует частоту кадров.
clock = pygame.time.Clock()
# В конце цикла
clock.tick(60) # Ограничивает цикл 60 кадрами в секунду
Метод tick(framerate) задерживает выполнение, чтобы достичь указанной частоты. Возвращает время в миллисекундах с прошлого вызова.
Функция pygame.time.get_ticks() возвращает время в миллисекундах с момента инициализации pygame.init(). Используется для таймеров.
start_time = pygame.time.get_ticks()
# В цикле
current_time = pygame.time.get_ticks()
if current_time - start_time > 1000:
print("Прошла секунда")
start_time = current_time
Функция pygame.time.delay(ms) приостанавливает программу на указанное время. pygame.time.wait(ms) работает аналогично, но блокирует события.
Изображения и звук
Модуль pygame.image загружает файлы в поверхности.
image = pygame.image.load("player.png")
# Конвертация формата для скорости
image = image.convert()
# Конвертация с прозрачностью
image = image.convert_alpha()
Поддерживемые форматы: PNG, JPG, BMP, GIF.
Модуль pygame.mixer управляет аудио.
| Класс/Функция | Описание |
|---|---|
pygame.mixer.Sound(file) | Загружает звуковой эффект в память |
sound.play() | Воспроизводит звук |
sound.stop() | Останавливает звук |
sound.set_volume(val) | Устанавливает громкость (0.0 - 1.0) |
pygame.mixer.music.load(file) | Загружает фоновую музыку (поток) |
pygame.mixer.music.play(loops) | Запускает музыку. loops=-1 для бесконечности |
pygame.mixer.music.stop() | Останавливает музыку |
pygame.mixer.music.set_volume(val) | Громкость музыки |
Пример использования:
jump_sound = pygame.mixer.Sound("jump.wav")
jump_sound.play()
pygame.mixer.music.load("bgm.mp3")
pygame.mixer.music.play(-1)
Цикл игры (Game Loop)
Игровой цикл — фундаментальная структура любой интерактивной программы. В Pygame он реализуется вручную и состоит из трёх этапов:
- Обработка событий (Event Handling)
- Обновление состояния игры (State Update)
- Отрисовка (Rendering) Пример базового цикла:
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Обновление логики
update_game_state()
# Отрисовка
screen.fill((0, 0, 0))
draw_objects(screen)
pygame.display.flip()
Цикл выполняется с максимально возможной частотой, обычно ограниченной с помощью pygame.time.Clock, чтобы обеспечить стабильный FPS (например, 60 кадров в секунду).
События (Events)
Pygame использует систему очереди событий. Все внешние воздействия — нажатия клавиш, движения мыши, закрытие окна — помещаются в очередь и обрабатываются в цикле через pygame.event.get().
События являются объектами с атрибутами type (тип события) и, опционально, дополнительными данными (key, pos, button и т.д.). Это позволяет реализовать реактивную архитектуру, где игра реагирует на пользовательский ввод асинхронно.
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player.jump()
Спрайты (Sprites)
Pygame использует объектно-ориентированную структуру. Использование классов упрощает управление множеством объектов. Класс наследуется от pygame.sprite.Sprite.
Базовый класс спрайта:
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((50, 50))
self.image.fill((0, 0, 255))
self.rect = self.image.get_rect()
self.rect.center = (400, 300)
self.speed = 5
def update(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.rect.x -= self.speed
if keys[pygame.K_RIGHT]:
self.rect.x += self.speed
if keys[pygame.K_UP]:
self.rect.y -= self.speed
if keys[pygame.K_DOWN]:
self.rect.y += self.speed
Спрайт в Pygame — это визуальный объект, обычно наследующий от класса pygame.sprite.Sprite. Спрайты объединяют графическое представление (Surface) и положение (Rect), а также могут содержать логику поведения.
Pygame предоставляет систему групп (pygame.sprite.Group) для эффективного управления коллекциями спрайтов: отрисовки, обновления, проверки столкновений.
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((50, 50))
self.image.fill((255, 0, 0))
self.rect = self.image.get_rect()
self.rect.center = (400, 300)
def update(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.rect.x -= 5
Группировка спрайтов позволяет отделить логику от представления и упрощает управление сотнями объектов.
Класс pygame.sprite.Group хранит списки спрайтов. Позволяет обновлять и отрисовывать все объекты одним вызовом.
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
# В цикле
all_sprites.update()
all_sprites.draw(screen)
Группы поддерживают проверку столкновений.
hits = pygame.sprite.spritecollide(player, enemies, False)
Параметр False указывает не удалять спрайт при столкновении.
Столкновения (Collisions)
Pygame предоставляет несколько методов для обнаружения пересечений между прямоугольниками (pygame.Rect). Основные функции:
- rect1.colliderect(rect2) — проверяет пересечение двух прямоугольников.
- pygame.sprite.collide_rect(sprite1, sprite2) — то же, для спрайтов.
- pygame.sprite.spritecollide(sprite, group, dokill) — проверяет столкновение спрайта с группой.
Для более сложных форм используются маски (pygame.mask.from_surface()), обеспечивающие пиксельно-точное обнаружение столкновений, хотя и с большими вычислительными затратами.
Звуки и музыка
Pygame поддерживает воспроизведение аудиофайлов через модули pygame.mixer и pygame.mixer.music.
- pygame.mixer.Sound — для коротких эффектов (выстрел, прыжок).
- pygame.mixer.music — для фоновой музыки (поддерживает потоковое воспроизведение).
jump_sound = pygame.mixer.Sound("jump.wav")
jump_sound.play()
pygame.mixer.music.load("background.mp3")
pygame.mixer.music.play(-1) # зацикливание
Поддерживаются форматы WAV, MP3, OGG. Однако качество и стабильность воспроизведения зависят от платформы и бэкенда SDL.
Пример: реализация «Змейки». Это классический пример, демонстрирующий работу с циклом, вводом, отрисовкой и логикой столкновений.
Основные компоненты:
- Состояние змейки: список координат её сегментов.
- Направление движения: вектор (dx, dy).
- Еда: случайно генерируемая позиция.
- Условия завершения: выход за границы поля или самопересечение.
Полный код:
import pygame
import random
pygame.init()
white = (255, 255, 255)
black = (0, 0, 0)
red = (213, 50, 80)
green = (0, 255, 0)
blue = (50, 153, 213)
width = 600
height = 400
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption('Змейка')
clock = pygame.time.Clock()
snake_block = 10
snake_speed = 15
font_style = pygame.font.SysFont("bahnschrift", 25)
score_font = pygame.font.SysFont("comicsansms", 35)
def your_score(score):
value = score_font.render("Счёт: " + str(score), True, black)
screen.blit(value, [0, 0])
def our_snake(snake_block, snake_list):
for x in snake_list:
pygame.draw.rect(screen, green, [x[0], x[1], snake_block, snake_block])
def message(msg, color):
mesg = font_style.render(msg, True, color)
screen.blit(mesg, [width / 6, height / 3])
def gameLoop():
game_over = False
game_close = False
x1 = width / 2
y1 = height / 2
x1_change = 0
y1_change = 0
snake_list = []
length_of_snake = 1
foodx = round(random.randrange(0, width - snake_block) / 10.0) * 10.0
foody = round(random.randrange(0, height - snake_block) / 10.0) * 10.0
while not game_over:
while game_close:
screen.fill(white)
message("Ты проиграл! C - продолжить, Q - выйти", red)
your_score(length_of_snake - 1)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
game_over = True
game_close = False
if event.key == pygame.K_c:
gameLoop()
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_over = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x1_change = -snake_block
y1_change = 0
elif event.key == pygame.K_RIGHT:
x1_change = snake_block
y1_change = 0
elif event.key == pygame.K_UP:
y1_change = -snake_block
x1_change = 0
elif event.key == pygame.K_DOWN:
y1_change = snake_block
x1_change = 0
if x1 >= width or x1 < 0 or y1 >= height or y1 < 0:
game_close = True
x1 += x1_change
y1 += y1_change
screen.fill(white)
pygame.draw.rect(screen, red, [foodx, foody, snake_block, snake_block])
snake_head = [x1, y1]
snake_list.append(snake_head)
if len(snake_list) > length_of_snake:
del snake_list[0]
for x in snake_list[:-1]:
if x == snake_head:
game_close = True
our_snake(snake_block, snake_list)
your_score(length_of_snake - 1)
pygame.display.update()
if x1 == foodx and y1 == foody:
foodx = round(random.randrange(0, width - snake_block) / 10.0) * 10.0
foody = round(random.randrange(0, height - snake_block) / 10.0) * 10.0
length_of_snake += 1
clock.tick(snake_speed)
pygame.quit()
quit()
gameLoop()
Pygame — обучающая платформа, позволяющая освоить ключевые концепции геймдева: игровой цикл, обработку ввода, управление состоянием, коллизии и отрисовку. Он даёт контроль над каждым аспектом, что способствует глубокому пониманию механизмов, лежащих в основе интерактивных приложений.
Для серьёзных проектов рекомендуется использовать специализированные игровые движки: Godot, Unity, Unreal. Однако для обучения, прототипирования или реализации простых 2D-игр Python с Pygame остаётся практичным и доступным выбором.
Практические примеры реализации
Вывод текста на экран
Создание функции для централизованного вывода текста упрощает код.
def draw_text(surface, text, size, x, y):
font = pygame.font.SysFont("serif", size)
text_surface = font.render(text, True, (255, 255, 255))
text_rect = text_surface.get_rect()
text_rect.midtop = (x, y)
surface.blit(text_surface, text_rect)
Создание спрайта игрока с анимацией
Анимация реализуется через смену изображений по таймеру.
class AnimatedPlayer(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.frames = []
for i in range(4):
img = pygame.image.load(f"player_{i}.png").convert_alpha()
self.frames.append(img)
self.image = self.frames[0]
self.rect = self.image.get_rect()
self.frame_index = 0
self.last_update = 0
self.frame_rate = 100 # мс между кадрами
def update(self):
now = pygame.time.get_ticks()
if now - self.last_update > self.frame_rate:
self.last_update = now
self.frame_index = (self.frame_index + 1) % len(self.frames)
self.image = self.frames[self.frame_index]
Спрайт противника и движение
Противники двигаются автоматически вниз.
class Enemy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((30, 30))
self.image.fill((255, 0, 0))
self.rect = self.image.get_rect()
self.rect.x = random.randrange(0, 800)
self.rect.y = random.randrange(-100, -40)
self.speed_y = random.randrange(2, 8)
def update(self):
self.rect.y += self.speed_y
if self.rect.top > 600:
self.rect.x = random.randrange(0, 800)
self.rect.y = random.randrange(-100, -40)
self.speed_y = random.randrange(2, 8)
Платформа с коллизией
Проверка столкновения игрока с платформой требует проверки направления движения.
class Platform(pygame.sprite.Sprite):
def __init__(self, x, y, w, h):
super().__init__()
self.image = pygame.Surface((w, h))
self.image.fill((100, 100, 100))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
# В цикле игрока
hits = pygame.sprite.spritecollide(player, platforms, False)
if hits:
if player.vel_y > 0: # Падение вниз
player.rect.bottom = hits[0].rect.top
player.vel_y = 0
player.jumping = False
Пуля или снаряд
Пули создаются при нажатии клавиши и летят вперёд.
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.Surface((10, 5))
self.image.fill((255, 255, 0))
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.speed = 10
def update(self):
self.rect.x += self.speed
if self.rect.left > 800:
self.kill() # Удаляет спрайт из всех групп
# Стрельба
class Player(pygame.sprite.Sprite):
def update(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
bullet = Bullet(self.rect.centerx, self.rect.centery)
all_sprites.add(bullet)
bullets.add(bullet)
Бонусы и сбор предметов
Бонусы исчезают при касании игрока.
class Bonus(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((20, 20))
self.image.fill((0, 255, 0))
self.rect = self.image.get_rect()
self.rect.x = random.randrange(0, 800)
self.rect.y = random.randrange(-100, -40)
self.speed_y = 3
def update(self):
self.rect.y += self.speed_y
if self.rect.top > 600:
self.kill()
# В главном цикле
hits = pygame.sprite.spritecollide(player, bonuses, True) # True удаляет бонус
for hit in hits:
score += 10
Реакция на клик мыши
Спавн объекта в месте клика.
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: # Левая кнопка
pos = pygame.mouse.get_pos()
effect = Effect(pos[0], pos[1])
all_sprites.add(effect)
Передвижение спрайта с ускорением
Использование векторов скорости и ускорения.
class PhysicsPlayer(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((50, 50))
self.image.fill((0, 0, 255))
self.rect = self.image.get_rect()
self.vel_x = 0
self.vel_y = 0
self.accel_x = 0
self.accel_y = 0
self.friction = -0.1
def update(self):
self.accel_x = 0
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.accel_x = -0.5
if keys[pygame.K_RIGHT]:
self.accel_x = 0.5
self.accel_x += self.vel_x * self.friction
self.vel_x += self.accel_x
self.rect.x += self.vel_x
# Ограничение скорости
if self.vel_x > 10: self.vel_x = 10
if self.vel_x < -10: self.vel_x = -10
Вывод сообщений и экран Game Over
Отрисовка поверх игрового процесса.
def draw_game_over(screen):
font = pygame.font.SysFont("Arial", 64)
text = font.render("GAME OVER", True, (255, 0, 0))
rect = text.get_rect(center=(400, 300))
screen.blit(text, rect)
font_small = pygame.font.SysFont("Arial", 24)
text_small = font_small.render("Нажмите R для рестарта", True, (255, 255, 255))
rect_small = text_small.get_rect(center=(400, 350))
screen.blit(text_small, rect_small)
Включение и выключение звуков
Управление микшером через клавиши.
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_m:
if pygame.mixer.music.get_busy():
pygame.mixer.music.pause()
else:
pygame.mixer.music.unpause()
if event.key == pygame.K_s:
volume = pygame.mixer.Sound.get_volume()
if volume > 0:
pygame.mixer.Sound.set_volume(0)
else:
pygame.mixer.Sound.set_volume(1)
Полный пример структуры игры
Код ниже объединяет описанные компоненты в единую систему.
import pygame
import random
WIDTH = 800
HEIGHT = 600
FPS = 60
# Цвета
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((50, 50))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
self.speed = 5
def update(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.rect.x -= self.speed
if keys[pygame.K_RIGHT]:
self.rect.x += self.speed
if keys[pygame.K_UP]:
self.rect.y -= self.speed
if keys[pygame.K_DOWN]:
self.rect.y += self.speed
# Границы экрана
if self.rect.left < 0: self.rect.left = 0
if self.rect.right > WIDTH: self.rect.right = WIDTH
if self.rect.top < 0: self.rect.top = 0
if self.rect.bottom > HEIGHT: self.rect.bottom = HEIGHT
class Enemy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((30, 30))
self.image.fill(RED)
self.rect = self.image.get_rect()
self.rect.x = random.randrange(WIDTH)
self.rect.y = random.randrange(-100, -40)
self.speed_y = random.randrange(2, 8)
def update(self):
self.rect.y += self.speed_y
if self.rect.top > HEIGHT:
self.rect.x = random.randrange(WIDTH)
self.rect.y = random.randrange(-100, -40)
self.speed_y = random.randrange(2, 8)
# Инициализация
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Игра")
clock = pygame.time.Clock()
# Группы
all_sprites = pygame.sprite.Group()
mobs = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
for i in range(8):
m = Enemy()
all_sprites.add(m)
mobs.add(m)
# Переменные
score = 0
font = pygame.font.SysFont("Arial", 24)
running = True
# Цикл
while running:
clock.tick(FPS)
# События
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Обновление
all_sprites.update()
# Коллизии
hits = pygame.sprite.spritecollide(player, mobs, False)
if hits:
running = False
# Отрисовка
screen.fill(BLACK)
all_sprites.draw(screen)
# Интерфейс
score_text = font.render(f"Счёт: {score}", True, WHITE)
screen.blit(score_text, (10, 10))
pygame.display.flip()
pygame.quit()
Play
Библиотека play — сторонняя библиотека, разработанная для упрощения создания 2D-игр и анимаций, особенно в образовательной среде. Она строится на основе Pygame Zero, но предлагает ещё более минималистичный API, ориентированный на начинающих. Установка проста:
pip install play
play следует императивной модели, в которой программа описывает поведение объектов в виде функций обратного вызова (update, draw). Главный цикл управления выполняется автоматически.
Центральным элементом являются спрайты — двухмерные объекты, обладающие позицией, размером, изображением и свойствами анимации. Спрайт создаётся как экземпляр класса play.new_image() или play.new_box():
ball = play.new_circle(color="red", x=0, y=0, radius=25)
Каждый спрайт имеет атрибуты:
- x, y — координаты центра.
- angle — угол поворота.
- size — масштаб относительно исходного изображения.
- image — если используется текстура.
- visible — видимость объекта.
Движение реализуется путём изменения атрибутов спрайтов в функции update(), которая вызывается каждый кадр (обычно 60 раз в секунду):
@play.repeat_forever
def do():
ball.x += 1
Также доступны встроенные методы, такие как move(), point_towards(), rotate().
Обнаружение столкновений (коллизий) выполняется с помощью метода is_touching(other_sprite):
if ball.is_touching(paddle):
ball.color = "blue"
Этот метод проверяет пересечение границ спрайтов (bounding boxes), что достаточно для большинства простых случаев, но не учитывает прозрачные области изображений.
play предоставляет простой доступ к состоянию клавиш:
if play.key_is_pressed("w"):
paddle.y += 5
Поддерживаются буквенные, цифровые и специальные клавиши ("space", "up", "left" и т. д.). Нет необходимости вручную управлять event loop — библиотека делает это автоматически.
Рассмотрим минимальную реализацию игры, где игрок должен перемещать платформу, чтобы поймать падающий шарик.
import play
# Создание объектов
paddle = play.new_box(color="green", x=0, y=-200, width=100, height=20)
ball = play.new_circle(color="red", x=0, y=200, radius=20)
score = play.new_text(words="Счёт: 0", x=0, y=250)
count = 0
@play.repeat_forever
def do():
global count
# Управление платформой
if play.key_is_pressed("left"):
paddle.x -= 5
if play.key_is_pressed("right"):
paddle.x += 5
# Движение шарика
ball.y -= 3
# Проверка коллизии
if ball.is_touching(paddle):
ball.y = 200 # Переместить шарик наверх
count += 1
score.words = f"Счёт: {count}"
# Перезапуск, если шарик упал
if ball.y < -250:
ball.y = 200
count = max(0, count - 1)
score.words = f"Счёт: {count}"
play.start_program()
Этот пример демонстрирует типичную структуру приложения в play: объявление объектов, реакция на события в цикле repeat_forever, использование глобального состояния.